1 module hip.api.net.controller;
2 public import hip.api.net.hipnet;
3 
4 /**
5 Example usage
6 ```d
7 struct Action
8 {
9     ubyte fromX, fromY, toX, toY;
10 }
11 
12 struct BoardState
13 {
14     Action[] actions;
15 }
16 struct AssignColor
17 {
18     black,
19     white
20 }
21 
22 alias ChessNetController = NetController!(MarkNetData!(
23     Action,
24     AssignColor,
25     BoardState
26 ));
27 
28 ChessNetController c = new ChessNetController(getNetworkInterface);
29 c.connect(NetIPAddress("127.0.0.1", 10_000));
30 c.on_connect(()
31 {
32     c.sendData([BoardState()]);
33 });
34 c.on_disconnect(()
35 {
36     logg("Waiting for other player to connect...");
37 });
38 
39 with(c.poll)
40 {
41     switch(typeID)
42     {
43         case Types.disconnect: //Disconnect event has no data
44             break;
45         case Types.Action:
46             Action act = getAction();
47             break;
48         case Types.AssignColor:
49             AssignColor c = getAssignColor();
50             break;
51         default: //Received an invalid typeID.
52             break;
53     }
54 }
55 ```
56 */
57 class NetController(alias NetData)
58 {
59     import std.traits:isInstanceOf;
60     import hip.api.net.server;
61     import hip.util.reflection;
62     static assert(isInstanceOf!(MarkNetData, NetData), "NetController only accepts NetData as an input.");
63 
64     struct NetControllerResult
65     {
66         NetData.idType typeID;
67         ubyte[] data;
68 
69         static alias Types = NetData.Types;
70 
71         ///Reserved for the destructor freeing buffer
72         private NetBuffer* buffer;
73         ///Reserved for the destructor freeing buffer.
74         private INetwork net;
75 
76         static foreach(t; NetData.RegisteredTypes)
77         {
78             mixin("t get",t.stringof,"(){return interpretNetworkData!(t)(buffer.header, data);}");
79         }
80 
81         ~this()
82         {
83             if(net !is null && buffer !is null)
84             {
85                 net.freeBuffer(buffer);
86                 net = null;
87                 buffer = null;
88                 data = null;
89                 typeID = 0;
90             }
91         }
92     }
93 
94     INetwork network;
95     protected void delegate() connectFn;
96     protected void delegate() disconnectFn;
97 
98     static foreach(m; __traits(allMembers, MarkedNetReservedTypes))
99     {
100         static if(Attributes!(MarkedNetReservedTypes, m).length)
101         {
102             static if(is(Attributes!(MarkedNetReservedTypes, m)[0].Response == void))
103             {
104                 mixin("protected void delegate() on_",m,"_fn;\n",
105                       "void on_",m,"(void delegate() v){ on_",m,"_fn = v;}"
106                 );
107             }
108             else
109             {
110                 mixin("protected void delegate(Attributes!(MarkedNetReservedTypes, m)[0].Response) on_",m,"_fn;\n",
111                       "void on_",m,"(void delegate(Attributes!(MarkedNetReservedTypes, m)[0].Response) v){ on_",m,"_fn = v;}"
112                 );
113             }
114         }
115     }
116 
117     static foreach(t; NetData.RegisteredTypes)
118     {
119         mixin("protected void delegate(t) ", t.stringof, "Handler;");
120         void registerHandler(void delegate(t) handler)
121         {
122             mixin(t.stringof, "Handler = handler;");
123         }
124 
125         /**
126          * Send the data. It can only send data via this NetController if it is part of one of the
127          * registered types from the MarkNetData
128          *
129          * Params:
130          *   data = Data that will be sent with a type information
131          */
132         void sendData(t data)
133         {
134             NetData.sendData(network, data);
135         }
136     }
137 
138     /**
139      * Use `targetConnectionID` if you wish to change the ID after connection.
140      *
141      * Params:
142      *   ip = The IPAddress to connect
143      *   id = The ID within the IP
144      * Returns: Status. Almost always will be pending
145      */
146     NetConnectStatus connect(NetIPAddress ip, uint id = NetID.server)
147     {
148         return network.connect(ip, (INetwork net)
149         {
150             net.send_connect();
151             // getConnectedClients();
152         }, id);
153     }
154 
155 
156     void getConnectedClients()
157     {
158         network.send_get_connected_clients();
159     }
160 
161     pragma(inline, true)
162     final uint getConnectionSelfID() const{return network.getConnectionSelfID();}
163 
164     pragma(inline, true)
165 	final void targetConnectionID(uint id){network.targetConnectionID(id);}
166 
167     pragma(inline, true)
168 	final uint targetConnectionID() const {return network.targetConnectionID();}
169 
170     pragma(inline, true)
171 	final bool isHost() const { return network.isHost; }
172 
173     this(INetwork network){this.network = network;}
174 
175     /**
176      * Polls the result from the socket/network data.
177      *
178      * WARNING: For getting correct data type, you MUST send the data using either NetData.sendData or
179      * NetController.sendData
180      *
181      * If you send through the socket, no type info will be sent together.
182      *
183      * Returns: A result that includes the typeID, sliced data and managed net buffer.
184      */
185     NetControllerResult poll()
186     {
187         if(network.getData())
188         {
189             NetBuffer* buffer = network.getCompletedBuffer();
190             ubyte[] data = buffer.getFinishedBuffer();
191             NetData.idType typeID = NetData.getDataFromBuffer(data);
192 
193             switch(typeID) with(NetControllerResult.Types)
194             {
195                 static foreach(i, t; NetData.RegisteredTypes)
196                 {
197                     case mixin(t):
198                         mixin("if(",t,"Handler !is null) ", t,"Handler(interpretNetworkData!(t)(buffer.header, data));");
199                         goto default;
200                 }
201                 static foreach(m; __traits(allMembers, MarkedNetReservedTypes))
202                 {
203                     static if(Attributes!(MarkedNetReservedTypes, m).length)
204                     {
205                         case mixin(m):
206                             alias fn = mixin("on_",m,"_fn");
207                             static if(is(Attributes!(MarkedNetReservedTypes, m)[0].Response == void))
208                             {
209                                 if(fn) fn();
210                             }
211                             else
212                             {
213                                 if(fn) fn(interpretNetworkData!(Parameters!(fn)[0])(buffer.header, data));
214                             }
215                             goto default;
216                     }
217                 }
218                 default:
219                     break;
220             }
221             return NetControllerResult(typeID, data, buffer, network);
222         }
223 
224         return NetControllerResult(NetData.Types.invalid);
225     }
226 }
227 
228 T getNetworkArray(T, E = typeof(T.init[0]))(ubyte[] data)
229 {
230     import hip.api.net.utils;
231     assert(data.length >= 4, "Data received is not an actual array since it is smaller than a length element.");
232     uint length = *(cast(uint*)data.ptr);
233     static if(hasDynamicArray!E) //Dynamic arrays for type of dynamic size
234     {
235         T temp;
236         temp.length = length;
237         size_t offset = uint.sizeof;
238         foreach(i; 0..length)
239         {
240             temp[i] = getNetworkStruct!(E)(data[offset..$]);
241             offset+= getSendTypeSize(temp[i]);
242         }
243         return temp;
244     }
245     else //Static size
246     {
247         assert(data.length >= 4 + length * E.sizeof, "Data received is not valid.Not enough space available for type "~E.stringof);
248         return (cast(E*)(data.ptr + uint.sizeof))[0..length].dup;
249     }
250 }
251 
252 
253 T getNetworkStruct(T)(ubyte[] data)
254 {
255     import hip.api.net.utils;
256     import std.traits:isDynamicArray;
257 
258     static if(hasDynamicArray!T)
259     {
260         T ret;
261         size_t offset;
262         foreach(ref v; ret.tupleof)
263         {
264             static if(isDynamicArray!(typeof(v)))
265                 v = getNetworkArray!(typeof(v))(data[offset..$]);
266             else
267                 v = getNetworkStruct!(typeof(v))(data[offset..$]);
268             offset+= getSendTypeSize(v);
269         }
270         return ret;
271     }
272     else
273     {
274         assert(data.length >= T.sizeof, "Data length is not enough to be interpreted as "~T.stringof);
275         return *cast(T*)data.ptr;
276     }
277 }
278 
279 T interpretNetworkData(T)(NetHeader header, ubyte[] data)
280 {
281 	import std.traits;
282 	static if(is(T == string))
283 	{
284 		if(header.type != NetDataType.text)
285 			throw new Exception("Unmatched data type when trying to get a string.");
286 		return cast(string)data;
287 	}
288     else static if(is(T == struct))
289     {
290         return getNetworkStruct!T(data);
291     }
292 	else
293 	{
294 		if(header.type != NetDataType.binary)
295 			throw new Exception("Unmatched data type when trying to get a binary.");
296 
297 		static if(isArray!T)
298 		{
299 			size_t arraySize = header.length / T.init[0].sizeof;
300 			static if(isStaticArray!T)
301 			{
302 				if(arraySize != T.init.length)
303 					throw new Exception("Received more data than the static array size of "~T.init.length.stringof);
304 				return (cast(T)data)[0..T.init.length];
305 			}
306 			else
307 			{
308 				return cast(T)data;
309 			}
310 		}
311 		else
312 		{
313 			return *cast(T*)data.ptr;
314 		}
315 	}
316 }